package com.meshnetics.gb.stack.core.apl;

import com.meshnetics.gb.stack.core.apl.type.*;
import com.meshnetics.gb.type.DataValue;
import com.meshnetics.gb.type.impl.additional.BaseImmutableObject;
import com.meshnetics.gb.type.impl.additional.HelperImmutableObject;
import com.meshnetics.gb.type.impl.additional.ToStringWrapper;
import com.meshnetics.gb.type.impl.additional.TypeInstantiable;

import java.nio.ByteBuffer;

/**
 * @author mmarkov
 * @author yaserchev
 */
public final class SimpleDescriptor extends BaseImmutableObject {
    public static final TypeInstantiable TYPE = new TypeInstantiable("simple descriptor", SimpleDescriptor.class);
    private static final String HEADER = "Simple descriptor";

    /** EndPointAddress of Application Object for this descriptor. */
    private final EndPointAddress endPointAddress;

    /** Application profile identifier. */
    private final ProfileId profileId;

    /** Application device identifier. */
    private final DeviceId deviceId;

    /** Application device version. */
    private final DeviceVersion deviceVersion;

    /** Application input clusters ids. */
    private final ClusterId[] inputClusters;

    /** Application output clusters ids. */
    private final ClusterId[] outputClusters;

    /**
     * Constructs SimpleDescriptor from the given parameters.
     *
     * @param endPointAddress EndPointAddress of Application Object for this descriptor
     * @param profileId application profile identifier
     * @param deviceId application device identifier
     * @param deviceVersion application device version
     * @param inputClusters application input clusters ids
     * @param outputClusters application output clusters ids
     */
    public SimpleDescriptor(EndPointAddress endPointAddress,
                            ProfileId profileId, DeviceId deviceId, DeviceVersion deviceVersion,
                            ClusterId[] inputClusters, ClusterId[] outputClusters) {
        super(TYPE, HEADER);
        if (endPointAddress == null) {
            throw new NullPointerException("parameter endPointAddress is null");
        }
        if (deviceId == null) {
            throw new NullPointerException("parameter deviceId is null");
        }
        if (deviceVersion == null) {
            throw new NullPointerException("parameter deviceVersion is null");
        }
        if (inputClusters == null) {
            throw new NullPointerException("parameter inputClusters is null");
        }
        if (outputClusters == null) {
            throw new NullPointerException("parameter outputClusters is null");
        }
        this.endPointAddress = endPointAddress;
        this.profileId = profileId;
        this.deviceId = deviceId;
        this.deviceVersion = deviceVersion;
        this.inputClusters = inputClusters;
        this.outputClusters = outputClusters;
    }

    public SimpleDescriptor(final ByteBuffer buffer) {
        super(TYPE, HEADER);
        endPointAddress = (EndPointAddress) TypeEndPointAddress.TYPE.deserialize(buffer);
        profileId = (ProfileId) TypeProfileId.TYPE.deserialize(buffer);
        deviceId = (DeviceId) TypeDeviceId.TYPE.deserialize(buffer);
        deviceVersion = (DeviceVersion) TypeDeviceVersion.TYPE.deserialize(buffer);
        Length length = new Length(buffer);
        inputClusters = new ClusterId[length.intValue()];
        System.arraycopy(HelperImmutableObject.restoreFromBuffer(TypeClusterId.TYPE, length.intValue(),
                buffer), 0, inputClusters, 0, length.intValue());
        length = new Length(buffer);
        outputClusters = new ClusterId[length.intValue()];
        System.arraycopy(HelperImmutableObject.restoreFromBuffer(TypeClusterId.TYPE, length.intValue(),
                buffer), 0, outputClusters, 0, length.intValue());
    }

    protected DataValue[] getValues() {
        DataValue[] tmp = new DataValue[]{
                endPointAddress,
                profileId,
                deviceId,
                deviceVersion,
                ToStringWrapper.create(new Length(inputClusters.length), "input clusters array ")
        };
        tmp = HelperImmutableObject.mergeArrays(tmp, inputClusters);
        tmp = HelperImmutableObject.mergeArrays(tmp, new DataValue[]{
                ToStringWrapper.create(new Length(outputClusters.length), "output clusters array ")
        });
        return HelperImmutableObject.mergeArrays(tmp, outputClusters);
    }
    /**
     * The endpoint field of the simple descriptor is eight bits in length and specifies the endpoint within
     * the node to which this description refers. Applications shall only use endpoints 1-240.
     *
     * @return number of endpoint in range from 1 to 240
     */
    public EndPointAddress getEndPointAddress() {
        return endPointAddress;
    }

    /**
     * Returns application profile identifier.
     *
     * @return application profile identifier
     */
    public ProfileId getProfileId() {
        return profileId;
    }

    /**
     * Returns application device identifier.
     *
     * @return application device identifier
     */
    public DeviceId getDeviceId() {
        return deviceId;
    }

    /**
     * Returns application device version.
     *
     * @return application device version
     */
    public DeviceVersion getDeviceVersion() {
        return deviceVersion;
    }

    /**
     * Returns application input clusters ids.
     *
     * @return application input clusters ids
     */
    public ClusterId[] getInputClusters() {
        return (ClusterId[]) inputClusters.clone();
    }

    /**
     * Returns application output clusters ids.
     *
     * @return application output clusters ids.
     */
    public ClusterId[] getOutputClusters() {
        return (ClusterId[]) outputClusters.clone();
    }

    // by yaserchev - toString is already realized in BaseImmutableObject
//    /**
//     * Returns string representation of this descriptor, i.e. all it's fields.
//     *
//     * @return string representation of this descriptor
//     */
//    public String toString() {
//        return "SimpleDescriptor{" +
//                "endPointAddress=" + endPointAddress +
//                ", profileId=" + profileId +
//                ", deviceId=" + deviceId +
//                ", deviceVersion=" + deviceVersion +
//                ", inputClusters=" + Arrays.toString(inputClusters) +
//                ", outputClusters=" + Arrays.toString(outputClusters) +
//                '}';
//    }
}
